/*
 * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package java.awt.geom;

import java.awt.Shape;
import java.awt.Rectangle;
import sun.awt.geom.Curve;
import java.io.Serializable;
import java.io.StreamCorruptedException;
import java.util.Arrays;

/**
 * The {@code Path2D} class provides a simple, yet flexible
 * shape which represents an arbitrary geometric path.
 * It can fully represent any path which can be iterated by the
 * {@link PathIterator} interface including all of its segment
 * types and winding rules and it implements all of the
 * basic hit testing methods of the {@link Shape} interface.
 * <p>
 * Use {@link Path2D.Float} when dealing with data that can be represented
 * and used with floating point precision.  Use {@link Path2D.Double}
 * for data that requires the accuracy or range of double precision.
 * <p>
 * {@code Path2D} provides exactly those facilities required for
 * basic construction and management of a geometric path and
 * implementation of the above interfaces with little added
 * interpretation.
 * If it is useful to manipulate the interiors of closed
 * geometric shapes beyond simple hit testing then the
 * {@link Area} class provides additional capabilities
 * specifically targeted at closed figures.
 * While both classes nominally implement the {@code Shape}
 * interface, they differ in purpose and together they provide
 * two useful views of a geometric shape where {@code Path2D}
 * deals primarily with a trajectory formed by path segments
 * and {@code Area} deals more with interpretation and manipulation
 * of enclosed regions of 2D geometric space.
 * <p>
 * The {@link PathIterator} interface has more detailed descriptions
 * of the types of segments that make up a path and the winding rules
 * that control how to determine which regions are inside or outside
 * the path.
 *
 * @author Jim Graham
 * @since 1.6
 */
public abstract class Path2D implements Shape, Cloneable {

  /**
   * An even-odd winding rule for determining the interior of
   * a path.
   *
   * @see PathIterator#WIND_EVEN_ODD
   * @since 1.6
   */
  public static final int WIND_EVEN_ODD = PathIterator.WIND_EVEN_ODD;

  /**
   * A non-zero winding rule for determining the interior of a
   * path.
   *
   * @see PathIterator#WIND_NON_ZERO
   * @since 1.6
   */
  public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO;

  // For code simplicity, copy these constants to our namespace
  // and cast them to byte constants for easy storage.
  private static final byte SEG_MOVETO = (byte) PathIterator.SEG_MOVETO;
  private static final byte SEG_LINETO = (byte) PathIterator.SEG_LINETO;
  private static final byte SEG_QUADTO = (byte) PathIterator.SEG_QUADTO;
  private static final byte SEG_CUBICTO = (byte) PathIterator.SEG_CUBICTO;
  private static final byte SEG_CLOSE = (byte) PathIterator.SEG_CLOSE;

  transient byte[] pointTypes;
  transient int numTypes;
  transient int numCoords;
  transient int windingRule;

  static final int INIT_SIZE = 20;
  static final int EXPAND_MAX = 500;
  static final int EXPAND_MAX_COORDS = EXPAND_MAX * 2;
  static final int EXPAND_MIN = 10; // ensure > 6 (cubics)

  /**
   * Constructs a new empty {@code Path2D} object.
   * It is assumed that the package sibling subclass that is
   * defaulting to this constructor will fill in all values.
   *
   * @since 1.6
   */
    /* private protected */
  Path2D() {
  }

  /**
   * Constructs a new {@code Path2D} object from the given
   * specified initial values.
   * This method is only intended for internal use and should
   * not be made public if the other constructors for this class
   * are ever exposed.
   *
   * @param rule the winding rule
   * @param initialTypes the size to make the initial array to store the path segment types
   * @since 1.6
   */
    /* private protected */
  Path2D(int rule, int initialTypes) {
    setWindingRule(rule);
    this.pointTypes = new byte[initialTypes];
  }

  abstract float[] cloneCoordsFloat(AffineTransform at);

  abstract double[] cloneCoordsDouble(AffineTransform at);

  abstract void append(float x, float y);

  abstract void append(double x, double y);

  abstract Point2D getPoint(int coordindex);

  abstract void needRoom(boolean needMove, int newCoords);

  abstract int pointCrossings(double px, double py);

  abstract int rectCrossings(double rxmin, double rymin,
      double rxmax, double rymax);

  static byte[] expandPointTypes(byte[] oldPointTypes, int needed) {
    final int oldSize = oldPointTypes.length;
    final int newSizeMin = oldSize + needed;
    if (newSizeMin < oldSize) {
      // hard overflow failure - we can't even accommodate
      // new items without overflowing
      throw new ArrayIndexOutOfBoundsException(
          "pointTypes exceeds maximum capacity !");
    }
    // growth algorithm computation
    int grow = oldSize;
    if (grow > EXPAND_MAX) {
      grow = Math.max(EXPAND_MAX, oldSize >> 3); // 1/8th min
    } else if (grow < EXPAND_MIN) {
      grow = EXPAND_MIN;
    }
    assert grow > 0;

    int newSize = oldSize + grow;
    if (newSize < newSizeMin) {
      // overflow in growth algorithm computation
      newSize = Integer.MAX_VALUE;
    }
    while (true) {
      try {
        // try allocating the larger array
        return Arrays.copyOf(oldPointTypes, newSize);
      } catch (OutOfMemoryError oome) {
        if (newSize == newSizeMin) {
          throw oome;
        }
      }
      newSize = newSizeMin + (newSize - newSizeMin) / 2;
    }
  }

  /**
   * The {@code Float} class defines a geometric path with
   * coordinates stored in single precision floating point.
   *
   * @since 1.6
   */
  public static class Float extends Path2D implements Serializable {

    transient float floatCoords[];

    /**
     * Constructs a new empty single precision {@code Path2D} object
     * with a default winding rule of {@link #WIND_NON_ZERO}.
     *
     * @since 1.6
     */
    public Float() {
      this(WIND_NON_ZERO, INIT_SIZE);
    }

    /**
     * Constructs a new empty single precision {@code Path2D} object
     * with the specified winding rule to control operations that
     * require the interior of the path to be defined.
     *
     * @param rule the winding rule
     * @see #WIND_EVEN_ODD
     * @see #WIND_NON_ZERO
     * @since 1.6
     */
    public Float(int rule) {
      this(rule, INIT_SIZE);
    }

    /**
     * Constructs a new empty single precision {@code Path2D} object
     * with the specified winding rule and the specified initial
     * capacity to store path segments.
     * This number is an initial guess as to how many path segments
     * will be added to the path, but the storage is expanded as
     * needed to store whatever path segments are added.
     *
     * @param rule the winding rule
     * @param initialCapacity the estimate for the number of path segments in the path
     * @see #WIND_EVEN_ODD
     * @see #WIND_NON_ZERO
     * @since 1.6
     */
    public Float(int rule, int initialCapacity) {
      super(rule, initialCapacity);
      floatCoords = new float[initialCapacity * 2];
    }

    /**
     * Constructs a new single precision {@code Path2D} object
     * from an arbitrary {@link Shape} object.
     * All of the initial geometry and the winding rule for this path are
     * taken from the specified {@code Shape} object.
     *
     * @param s the specified {@code Shape} object
     * @since 1.6
     */
    public Float(Shape s) {
      this(s, null);
    }

    /**
     * Constructs a new single precision {@code Path2D} object
     * from an arbitrary {@link Shape} object, transformed by an
     * {@link AffineTransform} object.
     * All of the initial geometry and the winding rule for this path are
     * taken from the specified {@code Shape} object and transformed
     * by the specified {@code AffineTransform} object.
     *
     * @param s the specified {@code Shape} object
     * @param at the specified {@code AffineTransform} object
     * @since 1.6
     */
    public Float(Shape s, AffineTransform at) {
      if (s instanceof Path2D) {
        Path2D p2d = (Path2D) s;
        setWindingRule(p2d.windingRule);
        this.numTypes = p2d.numTypes;
        // trim arrays:
        this.pointTypes = Arrays.copyOf(p2d.pointTypes, p2d.numTypes);
        this.numCoords = p2d.numCoords;
        this.floatCoords = p2d.cloneCoordsFloat(at);
      } else {
        PathIterator pi = s.getPathIterator(at);
        setWindingRule(pi.getWindingRule());
        this.pointTypes = new byte[INIT_SIZE];
        this.floatCoords = new float[INIT_SIZE * 2];
        append(pi, false);
      }
    }

    @Override
    float[] cloneCoordsFloat(AffineTransform at) {
      // trim arrays:
      float ret[];
      if (at == null) {
        ret = Arrays.copyOf(floatCoords, numCoords);
      } else {
        ret = new float[numCoords];
        at.transform(floatCoords, 0, ret, 0, numCoords / 2);
      }
      return ret;
    }

    @Override
    double[] cloneCoordsDouble(AffineTransform at) {
      // trim arrays:
      double ret[] = new double[numCoords];
      if (at == null) {
        for (int i = 0; i < numCoords; i++) {
          ret[i] = floatCoords[i];
        }
      } else {
        at.transform(floatCoords, 0, ret, 0, numCoords / 2);
      }
      return ret;
    }

    void append(float x, float y) {
      floatCoords[numCoords++] = x;
      floatCoords[numCoords++] = y;
    }

    void append(double x, double y) {
      floatCoords[numCoords++] = (float) x;
      floatCoords[numCoords++] = (float) y;
    }

    Point2D getPoint(int coordindex) {
      return new Point2D.Float(floatCoords[coordindex],
          floatCoords[coordindex + 1]);
    }

    @Override
    void needRoom(boolean needMove, int newCoords) {
      if ((numTypes == 0) && needMove) {
        throw new IllegalPathStateException("missing initial moveto " +
            "in path definition");
      }
      if (numTypes >= pointTypes.length) {
        pointTypes = expandPointTypes(pointTypes, 1);
      }
      if (numCoords > (floatCoords.length - newCoords)) {
        floatCoords = expandCoords(floatCoords, newCoords);
      }
    }

    static float[] expandCoords(float[] oldCoords, int needed) {
      final int oldSize = oldCoords.length;
      final int newSizeMin = oldSize + needed;
      if (newSizeMin < oldSize) {
        // hard overflow failure - we can't even accommodate
        // new items without overflowing
        throw new ArrayIndexOutOfBoundsException(
            "coords exceeds maximum capacity !");
      }
      // growth algorithm computation
      int grow = oldSize;
      if (grow > EXPAND_MAX_COORDS) {
        grow = Math.max(EXPAND_MAX_COORDS, oldSize >> 3); // 1/8th min
      } else if (grow < EXPAND_MIN) {
        grow = EXPAND_MIN;
      }
      assert grow > needed;

      int newSize = oldSize + grow;
      if (newSize < newSizeMin) {
        // overflow in growth algorithm computation
        newSize = Integer.MAX_VALUE;
      }
      while (true) {
        try {
          // try allocating the larger array
          return Arrays.copyOf(oldCoords, newSize);
        } catch (OutOfMemoryError oome) {
          if (newSize == newSizeMin) {
            throw oome;
          }
        }
        newSize = newSizeMin + (newSize - newSizeMin) / 2;
      }
    }

    /**
     * {@inheritDoc}
     *
     * @since 1.6
     */
    public final synchronized void moveTo(double x, double y) {
      if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) {
        floatCoords[numCoords - 2] = (float) x;
        floatCoords[numCoords - 1] = (float) y;
      } else {
        needRoom(false, 2);
        pointTypes[numTypes++] = SEG_MOVETO;
        floatCoords[numCoords++] = (float) x;
        floatCoords[numCoords++] = (float) y;
      }
    }

    /**
     * Adds a point to the path by moving to the specified
     * coordinates specified in float precision.
     * <p>
     * This method provides a single precision variant of
     * the double precision {@code moveTo()} method on the
     * base {@code Path2D} class.
     *
     * @param x the specified X coordinate
     * @param y the specified Y coordinate
     * @see Path2D#moveTo
     * @since 1.6
     */
    public final synchronized void moveTo(float x, float y) {
      if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) {
        floatCoords[numCoords - 2] = x;
        floatCoords[numCoords - 1] = y;
      } else {
        needRoom(false, 2);
        pointTypes[numTypes++] = SEG_MOVETO;
        floatCoords[numCoords++] = x;
        floatCoords[numCoords++] = y;
      }
    }

    /**
     * {@inheritDoc}
     *
     * @since 1.6
     */
    public final synchronized void lineTo(double x, double y) {
      needRoom(true, 2);
      pointTypes[numTypes++] = SEG_LINETO;
      floatCoords[numCoords++] = (float) x;
      floatCoords[numCoords++] = (float) y;
    }

    /**
     * Adds a point to the path by drawing a straight line from the
     * current coordinates to the new specified coordinates
     * specified in float precision.
     * <p>
     * This method provides a single precision variant of
     * the double precision {@code lineTo()} method on the
     * base {@code Path2D} class.
     *
     * @param x the specified X coordinate
     * @param y the specified Y coordinate
     * @see Path2D#lineTo
     * @since 1.6
     */
    public final synchronized void lineTo(float x, float y) {
      needRoom(true, 2);
      pointTypes[numTypes++] = SEG_LINETO;
      floatCoords[numCoords++] = x;
      floatCoords[numCoords++] = y;
    }

    /**
     * {@inheritDoc}
     *
     * @since 1.6
     */
    public final synchronized void quadTo(double x1, double y1,
        double x2, double y2) {
      needRoom(true, 4);
      pointTypes[numTypes++] = SEG_QUADTO;
      floatCoords[numCoords++] = (float) x1;
      floatCoords[numCoords++] = (float) y1;
      floatCoords[numCoords++] = (float) x2;
      floatCoords[numCoords++] = (float) y2;
    }

    /**
     * Adds a curved segment, defined by two new points, to the path by
     * drawing a Quadratic curve that intersects both the current
     * coordinates and the specified coordinates {@code (x2,y2)},
     * using the specified point {@code (x1,y1)} as a quadratic
     * parametric control point.
     * All coordinates are specified in float precision.
     * <p>
     * This method provides a single precision variant of
     * the double precision {@code quadTo()} method on the
     * base {@code Path2D} class.
     *
     * @param x1 the X coordinate of the quadratic control point
     * @param y1 the Y coordinate of the quadratic control point
     * @param x2 the X coordinate of the final end point
     * @param y2 the Y coordinate of the final end point
     * @see Path2D#quadTo
     * @since 1.6
     */
    public final synchronized void quadTo(float x1, float y1,
        float x2, float y2) {
      needRoom(true, 4);
      pointTypes[numTypes++] = SEG_QUADTO;
      floatCoords[numCoords++] = x1;
      floatCoords[numCoords++] = y1;
      floatCoords[numCoords++] = x2;
      floatCoords[numCoords++] = y2;
    }

    /**
     * {@inheritDoc}
     *
     * @since 1.6
     */
    public final synchronized void curveTo(double x1, double y1,
        double x2, double y2,
        double x3, double y3) {
      needRoom(true, 6);
      pointTypes[numTypes++] = SEG_CUBICTO;
      floatCoords[numCoords++] = (float) x1;
      floatCoords[numCoords++] = (float) y1;
      floatCoords[numCoords++] = (float) x2;
      floatCoords[numCoords++] = (float) y2;
      floatCoords[numCoords++] = (float) x3;
      floatCoords[numCoords++] = (float) y3;
    }

    /**
     * Adds a curved segment, defined by three new points, to the path by
     * drawing a B&eacute;zier curve that intersects both the current
     * coordinates and the specified coordinates {@code (x3,y3)},
     * using the specified points {@code (x1,y1)} and {@code (x2,y2)} as
     * B&eacute;zier control points.
     * All coordinates are specified in float precision.
     * <p>
     * This method provides a single precision variant of
     * the double precision {@code curveTo()} method on the
     * base {@code Path2D} class.
     *
     * @param x1 the X coordinate of the first B&eacute;zier control point
     * @param y1 the Y coordinate of the first B&eacute;zier control point
     * @param x2 the X coordinate of the second B&eacute;zier control point
     * @param y2 the Y coordinate of the second B&eacute;zier control point
     * @param x3 the X coordinate of the final end point
     * @param y3 the Y coordinate of the final end point
     * @see Path2D#curveTo
     * @since 1.6
     */
    public final synchronized void curveTo(float x1, float y1,
        float x2, float y2,
        float x3, float y3) {
      needRoom(true, 6);
      pointTypes[numTypes++] = SEG_CUBICTO;
      floatCoords[numCoords++] = x1;
      floatCoords[numCoords++] = y1;
      floatCoords[numCoords++] = x2;
      floatCoords[numCoords++] = y2;
      floatCoords[numCoords++] = x3;
      floatCoords[numCoords++] = y3;
    }

    int pointCrossings(double px, double py) {
      if (numTypes == 0) {
        return 0;
      }
      double movx, movy, curx, cury, endx, endy;
      float coords[] = floatCoords;
      curx = movx = coords[0];
      cury = movy = coords[1];
      int crossings = 0;
      int ci = 2;
      for (int i = 1; i < numTypes; i++) {
        switch (pointTypes[i]) {
          case PathIterator.SEG_MOVETO:
            if (cury != movy) {
              crossings +=
                  Curve.pointCrossingsForLine(px, py,
                      curx, cury,
                      movx, movy);
            }
            movx = curx = coords[ci++];
            movy = cury = coords[ci++];
            break;
          case PathIterator.SEG_LINETO:
            crossings +=
                Curve.pointCrossingsForLine(px, py,
                    curx, cury,
                    endx = coords[ci++],
                    endy = coords[ci++]);
            curx = endx;
            cury = endy;
            break;
          case PathIterator.SEG_QUADTO:
            crossings +=
                Curve.pointCrossingsForQuad(px, py,
                    curx, cury,
                    coords[ci++],
                    coords[ci++],
                    endx = coords[ci++],
                    endy = coords[ci++],
                    0);
            curx = endx;
            cury = endy;
            break;
          case PathIterator.SEG_CUBICTO:
            crossings +=
                Curve.pointCrossingsForCubic(px, py,
                    curx, cury,
                    coords[ci++],
                    coords[ci++],
                    coords[ci++],
                    coords[ci++],
                    endx = coords[ci++],
                    endy = coords[ci++],
                    0);
            curx = endx;
            cury = endy;
            break;
          case PathIterator.SEG_CLOSE:
            if (cury != movy) {
              crossings +=
                  Curve.pointCrossingsForLine(px, py,
                      curx, cury,
                      movx, movy);
            }
            curx = movx;
            cury = movy;
            break;
        }
      }
      if (cury != movy) {
        crossings +=
            Curve.pointCrossingsForLine(px, py,
                curx, cury,
                movx, movy);
      }
      return crossings;
    }

    int rectCrossings(double rxmin, double rymin,
        double rxmax, double rymax) {
      if (numTypes == 0) {
        return 0;
      }
      float coords[] = floatCoords;
      double curx, cury, movx, movy, endx, endy;
      curx = movx = coords[0];
      cury = movy = coords[1];
      int crossings = 0;
      int ci = 2;
      for (int i = 1;
          crossings != Curve.RECT_INTERSECTS && i < numTypes;
          i++) {
        switch (pointTypes[i]) {
          case PathIterator.SEG_MOVETO:
            if (curx != movx || cury != movy) {
              crossings =
                  Curve.rectCrossingsForLine(crossings,
                      rxmin, rymin,
                      rxmax, rymax,
                      curx, cury,
                      movx, movy);
            }
            // Count should always be a multiple of 2 here.
            // assert((crossings & 1) != 0);
            movx = curx = coords[ci++];
            movy = cury = coords[ci++];
            break;
          case PathIterator.SEG_LINETO:
            crossings =
                Curve.rectCrossingsForLine(crossings,
                    rxmin, rymin,
                    rxmax, rymax,
                    curx, cury,
                    endx = coords[ci++],
                    endy = coords[ci++]);
            curx = endx;
            cury = endy;
            break;
          case PathIterator.SEG_QUADTO:
            crossings =
                Curve.rectCrossingsForQuad(crossings,
                    rxmin, rymin,
                    rxmax, rymax,
                    curx, cury,
                    coords[ci++],
                    coords[ci++],
                    endx = coords[ci++],
                    endy = coords[ci++],
                    0);
            curx = endx;
            cury = endy;
            break;
          case PathIterator.SEG_CUBICTO:
            crossings =
                Curve.rectCrossingsForCubic(crossings,
                    rxmin, rymin,
                    rxmax, rymax,
                    curx, cury,
                    coords[ci++],
                    coords[ci++],
                    coords[ci++],
                    coords[ci++],
                    endx = coords[ci++],
                    endy = coords[ci++],
                    0);
            curx = endx;
            cury = endy;
            break;
          case PathIterator.SEG_CLOSE:
            if (curx != movx || cury != movy) {
              crossings =
                  Curve.rectCrossingsForLine(crossings,
                      rxmin, rymin,
                      rxmax, rymax,
                      curx, cury,
                      movx, movy);
            }
            curx = movx;
            cury = movy;
            // Count should always be a multiple of 2 here.
            // assert((crossings & 1) != 0);
            break;
        }
      }
      if (crossings != Curve.RECT_INTERSECTS &&
          (curx != movx || cury != movy)) {
        crossings =
            Curve.rectCrossingsForLine(crossings,
                rxmin, rymin,
                rxmax, rymax,
                curx, cury,
                movx, movy);
      }
      // Count should always be a multiple of 2 here.
      // assert((crossings & 1) != 0);
      return crossings;
    }

    /**
     * {@inheritDoc}
     *
     * @since 1.6
     */
    public final void append(PathIterator pi, boolean connect) {
      float coords[] = new float[6];
      while (!pi.isDone()) {
        switch (pi.currentSegment(coords)) {
          case SEG_MOVETO:
            if (!connect || numTypes < 1 || numCoords < 1) {
              moveTo(coords[0], coords[1]);
              break;
            }
            if (pointTypes[numTypes - 1] != SEG_CLOSE &&
                floatCoords[numCoords - 2] == coords[0] &&
                floatCoords[numCoords - 1] == coords[1]) {
              // Collapse out initial moveto/lineto
              break;
            }
            lineTo(coords[0], coords[1]);
            break;
          case SEG_LINETO:
            lineTo(coords[0], coords[1]);
            break;
          case SEG_QUADTO:
            quadTo(coords[0], coords[1],
                coords[2], coords[3]);
            break;
          case SEG_CUBICTO:
            curveTo(coords[0], coords[1],
                coords[2], coords[3],
                coords[4], coords[5]);
            break;
          case SEG_CLOSE:
            closePath();
            break;
        }
        pi.next();
        connect = false;
      }
    }

    /**
     * {@inheritDoc}
     *
     * @since 1.6
     */
    public final void transform(AffineTransform at) {
      at.transform(floatCoords, 0, floatCoords, 0, numCoords / 2);
    }

    /**
     * {@inheritDoc}
     *
     * @since 1.6
     */
    public final synchronized Rectangle2D getBounds2D() {
      float x1, y1, x2, y2;
      int i = numCoords;
      if (i > 0) {
        y1 = y2 = floatCoords[--i];
        x1 = x2 = floatCoords[--i];
        while (i > 0) {
          float y = floatCoords[--i];
          float x = floatCoords[--i];
          if (x < x1) {
            x1 = x;
          }
          if (y < y1) {
            y1 = y;
          }
          if (x > x2) {
            x2 = x;
          }
          if (y > y2) {
            y2 = y;
          }
        }
      } else {
        x1 = y1 = x2 = y2 = 0.0f;
      }
      return new Rectangle2D.Float(x1, y1, x2 - x1, y2 - y1);
    }

    /**
     * {@inheritDoc}
     * <p>
     * The iterator for this class is not multi-threaded safe,
     * which means that the {@code Path2D} class does not
     * guarantee that modifications to the geometry of this
     * {@code Path2D} object do not affect any iterations of
     * that geometry that are already in process.
     *
     * @since 1.6
     */
    public final PathIterator getPathIterator(AffineTransform at) {
      if (at == null) {
        return new CopyIterator(this);
      } else {
        return new TxIterator(this, at);
      }
    }

    /**
     * Creates a new object of the same class as this object.
     *
     * @return a clone of this instance.
     * @throws OutOfMemoryError if there is not enough memory.
     * @see java.lang.Cloneable
     * @since 1.6
     */
    public final Object clone() {
      // Note: It would be nice to have this return Path2D
      // but one of our subclasses (GeneralPath) needs to
      // offer "public Object clone()" for backwards
      // compatibility so we cannot restrict it further.
      // REMIND: Can we do both somehow?
      if (this instanceof GeneralPath) {
        return new GeneralPath(this);
      } else {
        return new Path2D.Float(this);
      }
    }

    /*
     * JDK 1.6 serialVersionUID
     */
    private static final long serialVersionUID = 6990832515060788886L;

    /**
     * Writes the default serializable fields to the
     * {@code ObjectOutputStream} followed by an explicit
     * serialization of the path segments stored in this
     * path.
     *
     * @serialData <a name="Path2DSerialData"><!-- --></a> <ol> <li>The default serializable fields.
     * There are no default serializable fields as of 1.6. <li>followed by a byte indicating the
     * storage type of the original object as a hint (SERIAL_STORAGE_FLT_ARRAY) <li>followed by an
     * integer indicating the number of path segments to follow (NP) or -1 to indicate an unknown
     * number of path segments follows <li>followed by an integer indicating the total number of
     * coordinates to follow (NC) or -1 to indicate an unknown number of coordinates follows (NC
     * should always be even since coordinates always appear in pairs representing an x,y pair)
     * <li>followed by a byte indicating the winding rule ({@link #WIND_EVEN_ODD WIND_EVEN_ODD} or
     * {@link #WIND_NON_ZERO WIND_NON_ZERO}) <li>followed by {@code NP} (or unlimited if {@code NP <
     * 0}) sets of values consisting of a single byte indicating a path segment type followed by one
     * or more pairs of float or double values representing the coordinates of the path segment
     * <li>followed by a byte indicating the end of the path (SERIAL_PATH_END). </ol> <p> The
     * following byte value constants are used in the serialized form of {@code Path2D} objects:
     * <table> <tr> <th>Constant Name</th> <th>Byte Value</th> <th>Followed by</th>
     * <th>Description</th> </tr> <tr> <td>{@code SERIAL_STORAGE_FLT_ARRAY}</td> <td>0x30</td>
     * <td></td> <td>A hint that the original {@code Path2D} object stored the coordinates in a Java
     * array of floats.</td> </tr> <tr> <td>{@code SERIAL_STORAGE_DBL_ARRAY}</td> <td>0x31</td>
     * <td></td> <td>A hint that the original {@code Path2D} object stored the coordinates in a Java
     * array of doubles.</td> </tr> <tr> <td>{@code SERIAL_SEG_FLT_MOVETO}</td> <td>0x40</td> <td>2
     * floats</td> <td>A {@link #moveTo moveTo} path segment follows.</td> </tr> <tr> <td>{@code
     * SERIAL_SEG_FLT_LINETO}</td> <td>0x41</td> <td>2 floats</td> <td>A {@link #lineTo lineTo} path
     * segment follows.</td> </tr> <tr> <td>{@code SERIAL_SEG_FLT_QUADTO}</td> <td>0x42</td> <td>4
     * floats</td> <td>A {@link #quadTo quadTo} path segment follows.</td> </tr> <tr> <td>{@code
     * SERIAL_SEG_FLT_CUBICTO}</td> <td>0x43</td> <td>6 floats</td> <td>A {@link #curveTo curveTo}
     * path segment follows.</td> </tr> <tr> <td>{@code SERIAL_SEG_DBL_MOVETO}</td> <td>0x50</td>
     * <td>2 doubles</td> <td>A {@link #moveTo moveTo} path segment follows.</td> </tr> <tr>
     * <td>{@code SERIAL_SEG_DBL_LINETO}</td> <td>0x51</td> <td>2 doubles</td> <td>A {@link #lineTo
     * lineTo} path segment follows.</td> </tr> <tr> <td>{@code SERIAL_SEG_DBL_QUADTO}</td>
     * <td>0x52</td> <td>4 doubles</td> <td>A {@link #curveTo curveTo} path segment follows.</td>
     * </tr> <tr> <td>{@code SERIAL_SEG_DBL_CUBICTO}</td> <td>0x53</td> <td>6 doubles</td> <td>A
     * {@link #curveTo curveTo} path segment follows.</td> </tr> <tr> <td>{@code
     * SERIAL_SEG_CLOSE}</td> <td>0x60</td> <td></td> <td>A {@link #closePath closePath} path
     * segment.</td> </tr> <tr> <td>{@code SERIAL_PATH_END}</td> <td>0x61</td> <td></td> <td>There
     * are no more path segments following.</td> </table>
     * @since 1.6
     */
    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {
      super.writeObject(s, false);
    }

    /**
     * Reads the default serializable fields from the
     * {@code ObjectInputStream} followed by an explicit
     * serialization of the path segments stored in this
     * path.
     * <p>
     * There are no default serializable fields as of 1.6.
     * <p>
     * The serial data for this object is described in the
     * writeObject method.
     *
     * @since 1.6
     */
    private void readObject(java.io.ObjectInputStream s)
        throws java.lang.ClassNotFoundException, java.io.IOException {
      super.readObject(s, false);
    }

    static class CopyIterator extends Path2D.Iterator {

      float floatCoords[];

      CopyIterator(Path2D.Float p2df) {
        super(p2df);
        this.floatCoords = p2df.floatCoords;
      }

      public int currentSegment(float[] coords) {
        int type = path.pointTypes[typeIdx];
        int numCoords = curvecoords[type];
        if (numCoords > 0) {
          System.arraycopy(floatCoords, pointIdx,
              coords, 0, numCoords);
        }
        return type;
      }

      public int currentSegment(double[] coords) {
        int type = path.pointTypes[typeIdx];
        int numCoords = curvecoords[type];
        if (numCoords > 0) {
          for (int i = 0; i < numCoords; i++) {
            coords[i] = floatCoords[pointIdx + i];
          }
        }
        return type;
      }
    }

    static class TxIterator extends Path2D.Iterator {

      float floatCoords[];
      AffineTransform affine;

      TxIterator(Path2D.Float p2df, AffineTransform at) {
        super(p2df);
        this.floatCoords = p2df.floatCoords;
        this.affine = at;
      }

      public int currentSegment(float[] coords) {
        int type = path.pointTypes[typeIdx];
        int numCoords = curvecoords[type];
        if (numCoords > 0) {
          affine.transform(floatCoords, pointIdx,
              coords, 0, numCoords / 2);
        }
        return type;
      }

      public int currentSegment(double[] coords) {
        int type = path.pointTypes[typeIdx];
        int numCoords = curvecoords[type];
        if (numCoords > 0) {
          affine.transform(floatCoords, pointIdx,
              coords, 0, numCoords / 2);
        }
        return type;
      }
    }

  }

  /**
   * The {@code Double} class defines a geometric path with
   * coordinates stored in double precision floating point.
   *
   * @since 1.6
   */
  public static class Double extends Path2D implements Serializable {

    transient double doubleCoords[];

    /**
     * Constructs a new empty double precision {@code Path2D} object
     * with a default winding rule of {@link #WIND_NON_ZERO}.
     *
     * @since 1.6
     */
    public Double() {
      this(WIND_NON_ZERO, INIT_SIZE);
    }

    /**
     * Constructs a new empty double precision {@code Path2D} object
     * with the specified winding rule to control operations that
     * require the interior of the path to be defined.
     *
     * @param rule the winding rule
     * @see #WIND_EVEN_ODD
     * @see #WIND_NON_ZERO
     * @since 1.6
     */
    public Double(int rule) {
      this(rule, INIT_SIZE);
    }

    /**
     * Constructs a new empty double precision {@code Path2D} object
     * with the specified winding rule and the specified initial
     * capacity to store path segments.
     * This number is an initial guess as to how many path segments
     * are in the path, but the storage is expanded as needed to store
     * whatever path segments are added to this path.
     *
     * @param rule the winding rule
     * @param initialCapacity the estimate for the number of path segments in the path
     * @see #WIND_EVEN_ODD
     * @see #WIND_NON_ZERO
     * @since 1.6
     */
    public Double(int rule, int initialCapacity) {
      super(rule, initialCapacity);
      doubleCoords = new double[initialCapacity * 2];
    }

    /**
     * Constructs a new double precision {@code Path2D} object
     * from an arbitrary {@link Shape} object.
     * All of the initial geometry and the winding rule for this path are
     * taken from the specified {@code Shape} object.
     *
     * @param s the specified {@code Shape} object
     * @since 1.6
     */
    public Double(Shape s) {
      this(s, null);
    }

    /**
     * Constructs a new double precision {@code Path2D} object
     * from an arbitrary {@link Shape} object, transformed by an
     * {@link AffineTransform} object.
     * All of the initial geometry and the winding rule for this path are
     * taken from the specified {@code Shape} object and transformed
     * by the specified {@code AffineTransform} object.
     *
     * @param s the specified {@code Shape} object
     * @param at the specified {@code AffineTransform} object
     * @since 1.6
     */
    public Double(Shape s, AffineTransform at) {
      if (s instanceof Path2D) {
        Path2D p2d = (Path2D) s;
        setWindingRule(p2d.windingRule);
        this.numTypes = p2d.numTypes;
        // trim arrays:
        this.pointTypes = Arrays.copyOf(p2d.pointTypes, p2d.numTypes);
        this.numCoords = p2d.numCoords;
        this.doubleCoords = p2d.cloneCoordsDouble(at);
      } else {
        PathIterator pi = s.getPathIterator(at);
        setWindingRule(pi.getWindingRule());
        this.pointTypes = new byte[INIT_SIZE];
        this.doubleCoords = new double[INIT_SIZE * 2];
        append(pi, false);
      }
    }

    @Override
    float[] cloneCoordsFloat(AffineTransform at) {
      // trim arrays:
      float ret[] = new float[numCoords];
      if (at == null) {
        for (int i = 0; i < numCoords; i++) {
          ret[i] = (float) doubleCoords[i];
        }
      } else {
        at.transform(doubleCoords, 0, ret, 0, numCoords / 2);
      }
      return ret;
    }

    @Override
    double[] cloneCoordsDouble(AffineTransform at) {
      // trim arrays:
      double ret[];
      if (at == null) {
        ret = Arrays.copyOf(doubleCoords, numCoords);
      } else {
        ret = new double[numCoords];
        at.transform(doubleCoords, 0, ret, 0, numCoords / 2);
      }
      return ret;
    }

    void append(float x, float y) {
      doubleCoords[numCoords++] = x;
      doubleCoords[numCoords++] = y;
    }

    void append(double x, double y) {
      doubleCoords[numCoords++] = x;
      doubleCoords[numCoords++] = y;
    }

    Point2D getPoint(int coordindex) {
      return new Point2D.Double(doubleCoords[coordindex],
          doubleCoords[coordindex + 1]);
    }

    @Override
    void needRoom(boolean needMove, int newCoords) {
      if ((numTypes == 0) && needMove) {
        throw new IllegalPathStateException("missing initial moveto " +
            "in path definition");
      }
      if (numTypes >= pointTypes.length) {
        pointTypes = expandPointTypes(pointTypes, 1);
      }
      if (numCoords > (doubleCoords.length - newCoords)) {
        doubleCoords = expandCoords(doubleCoords, newCoords);
      }
    }

    static double[] expandCoords(double[] oldCoords, int needed) {
      final int oldSize = oldCoords.length;
      final int newSizeMin = oldSize + needed;
      if (newSizeMin < oldSize) {
        // hard overflow failure - we can't even accommodate
        // new items without overflowing
        throw new ArrayIndexOutOfBoundsException(
            "coords exceeds maximum capacity !");
      }
      // growth algorithm computation
      int grow = oldSize;
      if (grow > EXPAND_MAX_COORDS) {
        grow = Math.max(EXPAND_MAX_COORDS, oldSize >> 3); // 1/8th min
      } else if (grow < EXPAND_MIN) {
        grow = EXPAND_MIN;
      }
      assert grow > needed;

      int newSize = oldSize + grow;
      if (newSize < newSizeMin) {
        // overflow in growth algorithm computation
        newSize = Integer.MAX_VALUE;
      }
      while (true) {
        try {
          // try allocating the larger array
          return Arrays.copyOf(oldCoords, newSize);
        } catch (OutOfMemoryError oome) {
          if (newSize == newSizeMin) {
            throw oome;
          }
        }
        newSize = newSizeMin + (newSize - newSizeMin) / 2;
      }
    }

    /**
     * {@inheritDoc}
     *
     * @since 1.6
     */
    public final synchronized void moveTo(double x, double y) {
      if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) {
        doubleCoords[numCoords - 2] = x;
        doubleCoords[numCoords - 1] = y;
      } else {
        needRoom(false, 2);
        pointTypes[numTypes++] = SEG_MOVETO;
        doubleCoords[numCoords++] = x;
        doubleCoords[numCoords++] = y;
      }
    }

    /**
     * {@inheritDoc}
     *
     * @since 1.6
     */
    public final synchronized void lineTo(double x, double y) {
      needRoom(true, 2);
      pointTypes[numTypes++] = SEG_LINETO;
      doubleCoords[numCoords++] = x;
      doubleCoords[numCoords++] = y;
    }

    /**
     * {@inheritDoc}
     *
     * @since 1.6
     */
    public final synchronized void quadTo(double x1, double y1,
        double x2, double y2) {
      needRoom(true, 4);
      pointTypes[numTypes++] = SEG_QUADTO;
      doubleCoords[numCoords++] = x1;
      doubleCoords[numCoords++] = y1;
      doubleCoords[numCoords++] = x2;
      doubleCoords[numCoords++] = y2;
    }

    /**
     * {@inheritDoc}
     *
     * @since 1.6
     */
    public final synchronized void curveTo(double x1, double y1,
        double x2, double y2,
        double x3, double y3) {
      needRoom(true, 6);
      pointTypes[numTypes++] = SEG_CUBICTO;
      doubleCoords[numCoords++] = x1;
      doubleCoords[numCoords++] = y1;
      doubleCoords[numCoords++] = x2;
      doubleCoords[numCoords++] = y2;
      doubleCoords[numCoords++] = x3;
      doubleCoords[numCoords++] = y3;
    }

    int pointCrossings(double px, double py) {
      if (numTypes == 0) {
        return 0;
      }
      double movx, movy, curx, cury, endx, endy;
      double coords[] = doubleCoords;
      curx = movx = coords[0];
      cury = movy = coords[1];
      int crossings = 0;
      int ci = 2;
      for (int i = 1; i < numTypes; i++) {
        switch (pointTypes[i]) {
          case PathIterator.SEG_MOVETO:
            if (cury != movy) {
              crossings +=
                  Curve.pointCrossingsForLine(px, py,
                      curx, cury,
                      movx, movy);
            }
            movx = curx = coords[ci++];
            movy = cury = coords[ci++];
            break;
          case PathIterator.SEG_LINETO:
            crossings +=
                Curve.pointCrossingsForLine(px, py,
                    curx, cury,
                    endx = coords[ci++],
                    endy = coords[ci++]);
            curx = endx;
            cury = endy;
            break;
          case PathIterator.SEG_QUADTO:
            crossings +=
                Curve.pointCrossingsForQuad(px, py,
                    curx, cury,
                    coords[ci++],
                    coords[ci++],
                    endx = coords[ci++],
                    endy = coords[ci++],
                    0);
            curx = endx;
            cury = endy;
            break;
          case PathIterator.SEG_CUBICTO:
            crossings +=
                Curve.pointCrossingsForCubic(px, py,
                    curx, cury,
                    coords[ci++],
                    coords[ci++],
                    coords[ci++],
                    coords[ci++],
                    endx = coords[ci++],
                    endy = coords[ci++],
                    0);
            curx = endx;
            cury = endy;
            break;
          case PathIterator.SEG_CLOSE:
            if (cury != movy) {
              crossings +=
                  Curve.pointCrossingsForLine(px, py,
                      curx, cury,
                      movx, movy);
            }
            curx = movx;
            cury = movy;
            break;
        }
      }
      if (cury != movy) {
        crossings +=
            Curve.pointCrossingsForLine(px, py,
                curx, cury,
                movx, movy);
      }
      return crossings;
    }

    int rectCrossings(double rxmin, double rymin,
        double rxmax, double rymax) {
      if (numTypes == 0) {
        return 0;
      }
      double coords[] = doubleCoords;
      double curx, cury, movx, movy, endx, endy;
      curx = movx = coords[0];
      cury = movy = coords[1];
      int crossings = 0;
      int ci = 2;
      for (int i = 1;
          crossings != Curve.RECT_INTERSECTS && i < numTypes;
          i++) {
        switch (pointTypes[i]) {
          case PathIterator.SEG_MOVETO:
            if (curx != movx || cury != movy) {
              crossings =
                  Curve.rectCrossingsForLine(crossings,
                      rxmin, rymin,
                      rxmax, rymax,
                      curx, cury,
                      movx, movy);
            }
            // Count should always be a multiple of 2 here.
            // assert((crossings & 1) != 0);
            movx = curx = coords[ci++];
            movy = cury = coords[ci++];
            break;
          case PathIterator.SEG_LINETO:
            endx = coords[ci++];
            endy = coords[ci++];
            crossings =
                Curve.rectCrossingsForLine(crossings,
                    rxmin, rymin,
                    rxmax, rymax,
                    curx, cury,
                    endx, endy);
            curx = endx;
            cury = endy;
            break;
          case PathIterator.SEG_QUADTO:
            crossings =
                Curve.rectCrossingsForQuad(crossings,
                    rxmin, rymin,
                    rxmax, rymax,
                    curx, cury,
                    coords[ci++],
                    coords[ci++],
                    endx = coords[ci++],
                    endy = coords[ci++],
                    0);
            curx = endx;
            cury = endy;
            break;
          case PathIterator.SEG_CUBICTO:
            crossings =
                Curve.rectCrossingsForCubic(crossings,
                    rxmin, rymin,
                    rxmax, rymax,
                    curx, cury,
                    coords[ci++],
                    coords[ci++],
                    coords[ci++],
                    coords[ci++],
                    endx = coords[ci++],
                    endy = coords[ci++],
                    0);
            curx = endx;
            cury = endy;
            break;
          case PathIterator.SEG_CLOSE:
            if (curx != movx || cury != movy) {
              crossings =
                  Curve.rectCrossingsForLine(crossings,
                      rxmin, rymin,
                      rxmax, rymax,
                      curx, cury,
                      movx, movy);
            }
            curx = movx;
            cury = movy;
            // Count should always be a multiple of 2 here.
            // assert((crossings & 1) != 0);
            break;
        }
      }
      if (crossings != Curve.RECT_INTERSECTS &&
          (curx != movx || cury != movy)) {
        crossings =
            Curve.rectCrossingsForLine(crossings,
                rxmin, rymin,
                rxmax, rymax,
                curx, cury,
                movx, movy);
      }
      // Count should always be a multiple of 2 here.
      // assert((crossings & 1) != 0);
      return crossings;
    }

    /**
     * {@inheritDoc}
     *
     * @since 1.6
     */
    public final void append(PathIterator pi, boolean connect) {
      double coords[] = new double[6];
      while (!pi.isDone()) {
        switch (pi.currentSegment(coords)) {
          case SEG_MOVETO:
            if (!connect || numTypes < 1 || numCoords < 1) {
              moveTo(coords[0], coords[1]);
              break;
            }
            if (pointTypes[numTypes - 1] != SEG_CLOSE &&
                doubleCoords[numCoords - 2] == coords[0] &&
                doubleCoords[numCoords - 1] == coords[1]) {
              // Collapse out initial moveto/lineto
              break;
            }
            lineTo(coords[0], coords[1]);
            break;
          case SEG_LINETO:
            lineTo(coords[0], coords[1]);
            break;
          case SEG_QUADTO:
            quadTo(coords[0], coords[1],
                coords[2], coords[3]);
            break;
          case SEG_CUBICTO:
            curveTo(coords[0], coords[1],
                coords[2], coords[3],
                coords[4], coords[5]);
            break;
          case SEG_CLOSE:
            closePath();
            break;
        }
        pi.next();
        connect = false;
      }
    }

    /**
     * {@inheritDoc}
     *
     * @since 1.6
     */
    public final void transform(AffineTransform at) {
      at.transform(doubleCoords, 0, doubleCoords, 0, numCoords / 2);
    }

    /**
     * {@inheritDoc}
     *
     * @since 1.6
     */
    public final synchronized Rectangle2D getBounds2D() {
      double x1, y1, x2, y2;
      int i = numCoords;
      if (i > 0) {
        y1 = y2 = doubleCoords[--i];
        x1 = x2 = doubleCoords[--i];
        while (i > 0) {
          double y = doubleCoords[--i];
          double x = doubleCoords[--i];
          if (x < x1) {
            x1 = x;
          }
          if (y < y1) {
            y1 = y;
          }
          if (x > x2) {
            x2 = x;
          }
          if (y > y2) {
            y2 = y;
          }
        }
      } else {
        x1 = y1 = x2 = y2 = 0.0;
      }
      return new Rectangle2D.Double(x1, y1, x2 - x1, y2 - y1);
    }

    /**
     * {@inheritDoc}
     * <p>
     * The iterator for this class is not multi-threaded safe,
     * which means that the {@code Path2D} class does not
     * guarantee that modifications to the geometry of this
     * {@code Path2D} object do not affect any iterations of
     * that geometry that are already in process.
     *
     * @param at an {@code AffineTransform}
     * @return a new {@code PathIterator} that iterates along the boundary of this {@code Shape} and
     * provides access to the geometry of this {@code Shape}'s outline
     * @since 1.6
     */
    public final PathIterator getPathIterator(AffineTransform at) {
      if (at == null) {
        return new CopyIterator(this);
      } else {
        return new TxIterator(this, at);
      }
    }

    /**
     * Creates a new object of the same class as this object.
     *
     * @return a clone of this instance.
     * @throws OutOfMemoryError if there is not enough memory.
     * @see java.lang.Cloneable
     * @since 1.6
     */
    public final Object clone() {
      // Note: It would be nice to have this return Path2D
      // but one of our subclasses (GeneralPath) needs to
      // offer "public Object clone()" for backwards
      // compatibility so we cannot restrict it further.
      // REMIND: Can we do both somehow?
      return new Path2D.Double(this);
    }

    /*
     * JDK 1.6 serialVersionUID
     */
    private static final long serialVersionUID = 1826762518450014216L;

    /**
     * Writes the default serializable fields to the
     * {@code ObjectOutputStream} followed by an explicit
     * serialization of the path segments stored in this
     * path.
     *
     * @serialData <a name="Path2DSerialData"><!-- --></a> <ol> <li>The default serializable fields.
     * There are no default serializable fields as of 1.6. <li>followed by a byte indicating the
     * storage type of the original object as a hint (SERIAL_STORAGE_DBL_ARRAY) <li>followed by an
     * integer indicating the number of path segments to follow (NP) or -1 to indicate an unknown
     * number of path segments follows <li>followed by an integer indicating the total number of
     * coordinates to follow (NC) or -1 to indicate an unknown number of coordinates follows (NC
     * should always be even since coordinates always appear in pairs representing an x,y pair)
     * <li>followed by a byte indicating the winding rule ({@link #WIND_EVEN_ODD WIND_EVEN_ODD} or
     * {@link #WIND_NON_ZERO WIND_NON_ZERO}) <li>followed by {@code NP} (or unlimited if {@code NP <
     * 0}) sets of values consisting of a single byte indicating a path segment type followed by one
     * or more pairs of float or double values representing the coordinates of the path segment
     * <li>followed by a byte indicating the end of the path (SERIAL_PATH_END). </ol> <p> The
     * following byte value constants are used in the serialized form of {@code Path2D} objects:
     * <table> <tr> <th>Constant Name</th> <th>Byte Value</th> <th>Followed by</th>
     * <th>Description</th> </tr> <tr> <td>{@code SERIAL_STORAGE_FLT_ARRAY}</td> <td>0x30</td>
     * <td></td> <td>A hint that the original {@code Path2D} object stored the coordinates in a Java
     * array of floats.</td> </tr> <tr> <td>{@code SERIAL_STORAGE_DBL_ARRAY}</td> <td>0x31</td>
     * <td></td> <td>A hint that the original {@code Path2D} object stored the coordinates in a Java
     * array of doubles.</td> </tr> <tr> <td>{@code SERIAL_SEG_FLT_MOVETO}</td> <td>0x40</td> <td>2
     * floats</td> <td>A {@link #moveTo moveTo} path segment follows.</td> </tr> <tr> <td>{@code
     * SERIAL_SEG_FLT_LINETO}</td> <td>0x41</td> <td>2 floats</td> <td>A {@link #lineTo lineTo} path
     * segment follows.</td> </tr> <tr> <td>{@code SERIAL_SEG_FLT_QUADTO}</td> <td>0x42</td> <td>4
     * floats</td> <td>A {@link #quadTo quadTo} path segment follows.</td> </tr> <tr> <td>{@code
     * SERIAL_SEG_FLT_CUBICTO}</td> <td>0x43</td> <td>6 floats</td> <td>A {@link #curveTo curveTo}
     * path segment follows.</td> </tr> <tr> <td>{@code SERIAL_SEG_DBL_MOVETO}</td> <td>0x50</td>
     * <td>2 doubles</td> <td>A {@link #moveTo moveTo} path segment follows.</td> </tr> <tr>
     * <td>{@code SERIAL_SEG_DBL_LINETO}</td> <td>0x51</td> <td>2 doubles</td> <td>A {@link #lineTo
     * lineTo} path segment follows.</td> </tr> <tr> <td>{@code SERIAL_SEG_DBL_QUADTO}</td>
     * <td>0x52</td> <td>4 doubles</td> <td>A {@link #curveTo curveTo} path segment follows.</td>
     * </tr> <tr> <td>{@code SERIAL_SEG_DBL_CUBICTO}</td> <td>0x53</td> <td>6 doubles</td> <td>A
     * {@link #curveTo curveTo} path segment follows.</td> </tr> <tr> <td>{@code
     * SERIAL_SEG_CLOSE}</td> <td>0x60</td> <td></td> <td>A {@link #closePath closePath} path
     * segment.</td> </tr> <tr> <td>{@code SERIAL_PATH_END}</td> <td>0x61</td> <td></td> <td>There
     * are no more path segments following.</td> </table>
     * @since 1.6
     */
    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {
      super.writeObject(s, true);
    }

    /**
     * Reads the default serializable fields from the
     * {@code ObjectInputStream} followed by an explicit
     * serialization of the path segments stored in this
     * path.
     * <p>
     * There are no default serializable fields as of 1.6.
     * <p>
     * The serial data for this object is described in the
     * writeObject method.
     *
     * @since 1.6
     */
    private void readObject(java.io.ObjectInputStream s)
        throws java.lang.ClassNotFoundException, java.io.IOException {
      super.readObject(s, true);
    }

    static class CopyIterator extends Path2D.Iterator {

      double doubleCoords[];

      CopyIterator(Path2D.Double p2dd) {
        super(p2dd);
        this.doubleCoords = p2dd.doubleCoords;
      }

      public int currentSegment(float[] coords) {
        int type = path.pointTypes[typeIdx];
        int numCoords = curvecoords[type];
        if (numCoords > 0) {
          for (int i = 0; i < numCoords; i++) {
            coords[i] = (float) doubleCoords[pointIdx + i];
          }
        }
        return type;
      }

      public int currentSegment(double[] coords) {
        int type = path.pointTypes[typeIdx];
        int numCoords = curvecoords[type];
        if (numCoords > 0) {
          System.arraycopy(doubleCoords, pointIdx,
              coords, 0, numCoords);
        }
        return type;
      }
    }

    static class TxIterator extends Path2D.Iterator {

      double doubleCoords[];
      AffineTransform affine;

      TxIterator(Path2D.Double p2dd, AffineTransform at) {
        super(p2dd);
        this.doubleCoords = p2dd.doubleCoords;
        this.affine = at;
      }

      public int currentSegment(float[] coords) {
        int type = path.pointTypes[typeIdx];
        int numCoords = curvecoords[type];
        if (numCoords > 0) {
          affine.transform(doubleCoords, pointIdx,
              coords, 0, numCoords / 2);
        }
        return type;
      }

      public int currentSegment(double[] coords) {
        int type = path.pointTypes[typeIdx];
        int numCoords = curvecoords[type];
        if (numCoords > 0) {
          affine.transform(doubleCoords, pointIdx,
              coords, 0, numCoords / 2);
        }
        return type;
      }
    }
  }

  /**
   * Adds a point to the path by moving to the specified
   * coordinates specified in double precision.
   *
   * @param x the specified X coordinate
   * @param y the specified Y coordinate
   * @since 1.6
   */
  public abstract void moveTo(double x, double y);

  /**
   * Adds a point to the path by drawing a straight line from the
   * current coordinates to the new specified coordinates
   * specified in double precision.
   *
   * @param x the specified X coordinate
   * @param y the specified Y coordinate
   * @since 1.6
   */
  public abstract void lineTo(double x, double y);

  /**
   * Adds a curved segment, defined by two new points, to the path by
   * drawing a Quadratic curve that intersects both the current
   * coordinates and the specified coordinates {@code (x2,y2)},
   * using the specified point {@code (x1,y1)} as a quadratic
   * parametric control point.
   * All coordinates are specified in double precision.
   *
   * @param x1 the X coordinate of the quadratic control point
   * @param y1 the Y coordinate of the quadratic control point
   * @param x2 the X coordinate of the final end point
   * @param y2 the Y coordinate of the final end point
   * @since 1.6
   */
  public abstract void quadTo(double x1, double y1,
      double x2, double y2);

  /**
   * Adds a curved segment, defined by three new points, to the path by
   * drawing a B&eacute;zier curve that intersects both the current
   * coordinates and the specified coordinates {@code (x3,y3)},
   * using the specified points {@code (x1,y1)} and {@code (x2,y2)} as
   * B&eacute;zier control points.
   * All coordinates are specified in double precision.
   *
   * @param x1 the X coordinate of the first B&eacute;zier control point
   * @param y1 the Y coordinate of the first B&eacute;zier control point
   * @param x2 the X coordinate of the second B&eacute;zier control point
   * @param y2 the Y coordinate of the second B&eacute;zier control point
   * @param x3 the X coordinate of the final end point
   * @param y3 the Y coordinate of the final end point
   * @since 1.6
   */
  public abstract void curveTo(double x1, double y1,
      double x2, double y2,
      double x3, double y3);

  /**
   * Closes the current subpath by drawing a straight line back to
   * the coordinates of the last {@code moveTo}.  If the path is already
   * closed then this method has no effect.
   *
   * @since 1.6
   */
  public final synchronized void closePath() {
    if (numTypes == 0 || pointTypes[numTypes - 1] != SEG_CLOSE) {
      needRoom(true, 0);
      pointTypes[numTypes++] = SEG_CLOSE;
    }
  }

  /**
   * Appends the geometry of the specified {@code Shape} object to the
   * path, possibly connecting the new geometry to the existing path
   * segments with a line segment.
   * If the {@code connect} parameter is {@code true} and the
   * path is not empty then any initial {@code moveTo} in the
   * geometry of the appended {@code Shape}
   * is turned into a {@code lineTo} segment.
   * If the destination coordinates of such a connecting {@code lineTo}
   * segment match the ending coordinates of a currently open
   * subpath then the segment is omitted as superfluous.
   * The winding rule of the specified {@code Shape} is ignored
   * and the appended geometry is governed by the winding
   * rule specified for this path.
   *
   * @param s the {@code Shape} whose geometry is appended to this path
   * @param connect a boolean to control whether or not to turn an initial {@code moveTo} segment
   * into a {@code lineTo} segment to connect the new geometry to the existing path
   * @since 1.6
   */
  public final void append(Shape s, boolean connect) {
    append(s.getPathIterator(null), connect);
  }

  /**
   * Appends the geometry of the specified
   * {@link PathIterator} object
   * to the path, possibly connecting the new geometry to the existing
   * path segments with a line segment.
   * If the {@code connect} parameter is {@code true} and the
   * path is not empty then any initial {@code moveTo} in the
   * geometry of the appended {@code Shape} is turned into a
   * {@code lineTo} segment.
   * If the destination coordinates of such a connecting {@code lineTo}
   * segment match the ending coordinates of a currently open
   * subpath then the segment is omitted as superfluous.
   * The winding rule of the specified {@code Shape} is ignored
   * and the appended geometry is governed by the winding
   * rule specified for this path.
   *
   * @param pi the {@code PathIterator} whose geometry is appended to this path
   * @param connect a boolean to control whether or not to turn an initial {@code moveTo} segment
   * into a {@code lineTo} segment to connect the new geometry to the existing path
   * @since 1.6
   */
  public abstract void append(PathIterator pi, boolean connect);

  /**
   * Returns the fill style winding rule.
   *
   * @return an integer representing the current winding rule.
   * @see #WIND_EVEN_ODD
   * @see #WIND_NON_ZERO
   * @see #setWindingRule
   * @since 1.6
   */
  public final synchronized int getWindingRule() {
    return windingRule;
  }

  /**
   * Sets the winding rule for this path to the specified value.
   *
   * @param rule an integer representing the specified winding rule
   * @throws IllegalArgumentException if {@code rule} is not either {@link #WIND_EVEN_ODD} or {@link
   * #WIND_NON_ZERO}
   * @see #getWindingRule
   * @since 1.6
   */
  public final void setWindingRule(int rule) {
    if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) {
      throw new IllegalArgumentException("winding rule must be " +
          "WIND_EVEN_ODD or " +
          "WIND_NON_ZERO");
    }
    windingRule = rule;
  }

  /**
   * Returns the coordinates most recently added to the end of the path
   * as a {@link Point2D} object.
   *
   * @return a {@code Point2D} object containing the ending coordinates of the path or {@code null}
   * if there are no points in the path.
   * @since 1.6
   */
  public final synchronized Point2D getCurrentPoint() {
    int index = numCoords;
    if (numTypes < 1 || index < 1) {
      return null;
    }
    if (pointTypes[numTypes - 1] == SEG_CLOSE) {
      loop:
      for (int i = numTypes - 2; i > 0; i--) {
        switch (pointTypes[i]) {
          case SEG_MOVETO:
            break loop;
          case SEG_LINETO:
            index -= 2;
            break;
          case SEG_QUADTO:
            index -= 4;
            break;
          case SEG_CUBICTO:
            index -= 6;
            break;
          case SEG_CLOSE:
            break;
        }
      }
    }
    return getPoint(index - 2);
  }

  /**
   * Resets the path to empty.  The append position is set back to the
   * beginning of the path and all coordinates and point types are
   * forgotten.
   *
   * @since 1.6
   */
  public final synchronized void reset() {
    numTypes = numCoords = 0;
  }

  /**
   * Transforms the geometry of this path using the specified
   * {@link AffineTransform}.
   * The geometry is transformed in place, which permanently changes the
   * boundary defined by this object.
   *
   * @param at the {@code AffineTransform} used to transform the area
   * @since 1.6
   */
  public abstract void transform(AffineTransform at);

  /**
   * Returns a new {@code Shape} representing a transformed version
   * of this {@code Path2D}.
   * Note that the exact type and coordinate precision of the return
   * value is not specified for this method.
   * The method will return a Shape that contains no less precision
   * for the transformed geometry than this {@code Path2D} currently
   * maintains, but it may contain no more precision either.
   * If the tradeoff of precision vs. storage size in the result is
   * important then the convenience constructors in the
   * {@link Path2D.Float#Path2D.Float(Shape, AffineTransform) Path2D.Float}
   * and
   * {@link Path2D.Double#Path2D.Double(Shape, AffineTransform) Path2D.Double}
   * subclasses should be used to make the choice explicit.
   *
   * @param at the {@code AffineTransform} used to transform a new {@code Shape}.
   * @return a new {@code Shape}, transformed with the specified {@code AffineTransform}.
   * @since 1.6
   */
  public final synchronized Shape createTransformedShape(AffineTransform at) {
    Path2D p2d = (Path2D) clone();
    if (at != null) {
      p2d.transform(at);
    }
    return p2d;
  }

  /**
   * {@inheritDoc}
   *
   * @since 1.6
   */
  public final Rectangle getBounds() {
    return getBounds2D().getBounds();
  }

  /**
   * Tests if the specified coordinates are inside the closed
   * boundary of the specified {@link PathIterator}.
   * <p>
   * This method provides a basic facility for implementors of
   * the {@link Shape} interface to implement support for the
   * {@link Shape#contains(double, double)} method.
   *
   * @param pi the specified {@code PathIterator}
   * @param x the specified X coordinate
   * @param y the specified Y coordinate
   * @return {@code true} if the specified coordinates are inside the specified {@code
   * PathIterator}; {@code false} otherwise
   * @since 1.6
   */
  public static boolean contains(PathIterator pi, double x, double y) {
    if (x * 0.0 + y * 0.0 == 0.0) {
            /* N * 0.0 is 0.0 only if N is finite.
             * Here we know that both x and y are finite.
             */
      int mask = (pi.getWindingRule() == WIND_NON_ZERO ? -1 : 1);
      int cross = Curve.pointCrossingsForPath(pi, x, y);
      return ((cross & mask) != 0);
    } else {
            /* Either x or y was infinite or NaN.
             * A NaN always produces a negative response to any test
             * and Infinity values cannot be "inside" any path so
             * they should return false as well.
             */
      return false;
    }
  }

  /**
   * Tests if the specified {@link Point2D} is inside the closed
   * boundary of the specified {@link PathIterator}.
   * <p>
   * This method provides a basic facility for implementors of
   * the {@link Shape} interface to implement support for the
   * {@link Shape#contains(Point2D)} method.
   *
   * @param pi the specified {@code PathIterator}
   * @param p the specified {@code Point2D}
   * @return {@code true} if the specified coordinates are inside the specified {@code
   * PathIterator}; {@code false} otherwise
   * @since 1.6
   */
  public static boolean contains(PathIterator pi, Point2D p) {
    return contains(pi, p.getX(), p.getY());
  }

  /**
   * {@inheritDoc}
   *
   * @since 1.6
   */
  public final boolean contains(double x, double y) {
    if (x * 0.0 + y * 0.0 == 0.0) {
            /* N * 0.0 is 0.0 only if N is finite.
             * Here we know that both x and y are finite.
             */
      if (numTypes < 2) {
        return false;
      }
      int mask = (windingRule == WIND_NON_ZERO ? -1 : 1);
      return ((pointCrossings(x, y) & mask) != 0);
    } else {
            /* Either x or y was infinite or NaN.
             * A NaN always produces a negative response to any test
             * and Infinity values cannot be "inside" any path so
             * they should return false as well.
             */
      return false;
    }
  }

  /**
   * {@inheritDoc}
   *
   * @since 1.6
   */
  public final boolean contains(Point2D p) {
    return contains(p.getX(), p.getY());
  }

  /**
   * Tests if the specified rectangular area is entirely inside the
   * closed boundary of the specified {@link PathIterator}.
   * <p>
   * This method provides a basic facility for implementors of
   * the {@link Shape} interface to implement support for the
   * {@link Shape#contains(double, double, double, double)} method.
   * <p>
   * This method object may conservatively return false in
   * cases where the specified rectangular area intersects a
   * segment of the path, but that segment does not represent a
   * boundary between the interior and exterior of the path.
   * Such segments could lie entirely within the interior of the
   * path if they are part of a path with a {@link #WIND_NON_ZERO}
   * winding rule or if the segments are retraced in the reverse
   * direction such that the two sets of segments cancel each
   * other out without any exterior area falling between them.
   * To determine whether segments represent true boundaries of
   * the interior of the path would require extensive calculations
   * involving all of the segments of the path and the winding
   * rule and are thus beyond the scope of this implementation.
   *
   * @param pi the specified {@code PathIterator}
   * @param x the specified X coordinate
   * @param y the specified Y coordinate
   * @param w the width of the specified rectangular area
   * @param h the height of the specified rectangular area
   * @return {@code true} if the specified {@code PathIterator} contains the specified rectangular
   * area; {@code false} otherwise.
   * @since 1.6
   */
  public static boolean contains(PathIterator pi,
      double x, double y, double w, double h) {
    if (java.lang.Double.isNaN(x + w) || java.lang.Double.isNaN(y + h)) {
            /* [xy]+[wh] is NaN if any of those values are NaN,
             * or if adding the two together would produce NaN
             * by virtue of adding opposing Infinte values.
             * Since we need to add them below, their sum must
             * not be NaN.
             * We return false because NaN always produces a
             * negative response to tests
             */
      return false;
    }
    if (w <= 0 || h <= 0) {
      return false;
    }
    int mask = (pi.getWindingRule() == WIND_NON_ZERO ? -1 : 2);
    int crossings = Curve.rectCrossingsForPath(pi, x, y, x + w, y + h);
    return (crossings != Curve.RECT_INTERSECTS &&
        (crossings & mask) != 0);
  }

  /**
   * Tests if the specified {@link Rectangle2D} is entirely inside the
   * closed boundary of the specified {@link PathIterator}.
   * <p>
   * This method provides a basic facility for implementors of
   * the {@link Shape} interface to implement support for the
   * {@link Shape#contains(Rectangle2D)} method.
   * <p>
   * This method object may conservatively return false in
   * cases where the specified rectangular area intersects a
   * segment of the path, but that segment does not represent a
   * boundary between the interior and exterior of the path.
   * Such segments could lie entirely within the interior of the
   * path if they are part of a path with a {@link #WIND_NON_ZERO}
   * winding rule or if the segments are retraced in the reverse
   * direction such that the two sets of segments cancel each
   * other out without any exterior area falling between them.
   * To determine whether segments represent true boundaries of
   * the interior of the path would require extensive calculations
   * involving all of the segments of the path and the winding
   * rule and are thus beyond the scope of this implementation.
   *
   * @param pi the specified {@code PathIterator}
   * @param r a specified {@code Rectangle2D}
   * @return {@code true} if the specified {@code PathIterator} contains the specified {@code
   * Rectangle2D}; {@code false} otherwise.
   * @since 1.6
   */
  public static boolean contains(PathIterator pi, Rectangle2D r) {
    return contains(pi, r.getX(), r.getY(), r.getWidth(), r.getHeight());
  }

  /**
   * {@inheritDoc}
   * <p>
   * This method object may conservatively return false in
   * cases where the specified rectangular area intersects a
   * segment of the path, but that segment does not represent a
   * boundary between the interior and exterior of the path.
   * Such segments could lie entirely within the interior of the
   * path if they are part of a path with a {@link #WIND_NON_ZERO}
   * winding rule or if the segments are retraced in the reverse
   * direction such that the two sets of segments cancel each
   * other out without any exterior area falling between them.
   * To determine whether segments represent true boundaries of
   * the interior of the path would require extensive calculations
   * involving all of the segments of the path and the winding
   * rule and are thus beyond the scope of this implementation.
   *
   * @since 1.6
   */
  public final boolean contains(double x, double y, double w, double h) {
    if (java.lang.Double.isNaN(x + w) || java.lang.Double.isNaN(y + h)) {
            /* [xy]+[wh] is NaN if any of those values are NaN,
             * or if adding the two together would produce NaN
             * by virtue of adding opposing Infinte values.
             * Since we need to add them below, their sum must
             * not be NaN.
             * We return false because NaN always produces a
             * negative response to tests
             */
      return false;
    }
    if (w <= 0 || h <= 0) {
      return false;
    }
    int mask = (windingRule == WIND_NON_ZERO ? -1 : 2);
    int crossings = rectCrossings(x, y, x + w, y + h);
    return (crossings != Curve.RECT_INTERSECTS &&
        (crossings & mask) != 0);
  }

  /**
   * {@inheritDoc}
   * <p>
   * This method object may conservatively return false in
   * cases where the specified rectangular area intersects a
   * segment of the path, but that segment does not represent a
   * boundary between the interior and exterior of the path.
   * Such segments could lie entirely within the interior of the
   * path if they are part of a path with a {@link #WIND_NON_ZERO}
   * winding rule or if the segments are retraced in the reverse
   * direction such that the two sets of segments cancel each
   * other out without any exterior area falling between them.
   * To determine whether segments represent true boundaries of
   * the interior of the path would require extensive calculations
   * involving all of the segments of the path and the winding
   * rule and are thus beyond the scope of this implementation.
   *
   * @since 1.6
   */
  public final boolean contains(Rectangle2D r) {
    return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
  }

  /**
   * Tests if the interior of the specified {@link PathIterator}
   * intersects the interior of a specified set of rectangular
   * coordinates.
   * <p>
   * This method provides a basic facility for implementors of
   * the {@link Shape} interface to implement support for the
   * {@link Shape#intersects(double, double, double, double)} method.
   * <p>
   * This method object may conservatively return true in
   * cases where the specified rectangular area intersects a
   * segment of the path, but that segment does not represent a
   * boundary between the interior and exterior of the path.
   * Such a case may occur if some set of segments of the
   * path are retraced in the reverse direction such that the
   * two sets of segments cancel each other out without any
   * interior area between them.
   * To determine whether segments represent true boundaries of
   * the interior of the path would require extensive calculations
   * involving all of the segments of the path and the winding
   * rule and are thus beyond the scope of this implementation.
   *
   * @param pi the specified {@code PathIterator}
   * @param x the specified X coordinate
   * @param y the specified Y coordinate
   * @param w the width of the specified rectangular coordinates
   * @param h the height of the specified rectangular coordinates
   * @return {@code true} if the specified {@code PathIterator} and the interior of the specified
   * set of rectangular coordinates intersect each other; {@code false} otherwise.
   * @since 1.6
   */
  public static boolean intersects(PathIterator pi,
      double x, double y, double w, double h) {
    if (java.lang.Double.isNaN(x + w) || java.lang.Double.isNaN(y + h)) {
            /* [xy]+[wh] is NaN if any of those values are NaN,
             * or if adding the two together would produce NaN
             * by virtue of adding opposing Infinte values.
             * Since we need to add them below, their sum must
             * not be NaN.
             * We return false because NaN always produces a
             * negative response to tests
             */
      return false;
    }
    if (w <= 0 || h <= 0) {
      return false;
    }
    int mask = (pi.getWindingRule() == WIND_NON_ZERO ? -1 : 2);
    int crossings = Curve.rectCrossingsForPath(pi, x, y, x + w, y + h);
    return (crossings == Curve.RECT_INTERSECTS ||
        (crossings & mask) != 0);
  }

  /**
   * Tests if the interior of the specified {@link PathIterator}
   * intersects the interior of a specified {@link Rectangle2D}.
   * <p>
   * This method provides a basic facility for implementors of
   * the {@link Shape} interface to implement support for the
   * {@link Shape#intersects(Rectangle2D)} method.
   * <p>
   * This method object may conservatively return true in
   * cases where the specified rectangular area intersects a
   * segment of the path, but that segment does not represent a
   * boundary between the interior and exterior of the path.
   * Such a case may occur if some set of segments of the
   * path are retraced in the reverse direction such that the
   * two sets of segments cancel each other out without any
   * interior area between them.
   * To determine whether segments represent true boundaries of
   * the interior of the path would require extensive calculations
   * involving all of the segments of the path and the winding
   * rule and are thus beyond the scope of this implementation.
   *
   * @param pi the specified {@code PathIterator}
   * @param r the specified {@code Rectangle2D}
   * @return {@code true} if the specified {@code PathIterator} and the interior of the specified
   * {@code Rectangle2D} intersect each other; {@code false} otherwise.
   * @since 1.6
   */
  public static boolean intersects(PathIterator pi, Rectangle2D r) {
    return intersects(pi, r.getX(), r.getY(), r.getWidth(), r.getHeight());
  }

  /**
   * {@inheritDoc}
   * <p>
   * This method object may conservatively return true in
   * cases where the specified rectangular area intersects a
   * segment of the path, but that segment does not represent a
   * boundary between the interior and exterior of the path.
   * Such a case may occur if some set of segments of the
   * path are retraced in the reverse direction such that the
   * two sets of segments cancel each other out without any
   * interior area between them.
   * To determine whether segments represent true boundaries of
   * the interior of the path would require extensive calculations
   * involving all of the segments of the path and the winding
   * rule and are thus beyond the scope of this implementation.
   *
   * @since 1.6
   */
  public final boolean intersects(double x, double y, double w, double h) {
    if (java.lang.Double.isNaN(x + w) || java.lang.Double.isNaN(y + h)) {
            /* [xy]+[wh] is NaN if any of those values are NaN,
             * or if adding the two together would produce NaN
             * by virtue of adding opposing Infinte values.
             * Since we need to add them below, their sum must
             * not be NaN.
             * We return false because NaN always produces a
             * negative response to tests
             */
      return false;
    }
    if (w <= 0 || h <= 0) {
      return false;
    }
    int mask = (windingRule == WIND_NON_ZERO ? -1 : 2);
    int crossings = rectCrossings(x, y, x + w, y + h);
    return (crossings == Curve.RECT_INTERSECTS ||
        (crossings & mask) != 0);
  }

  /**
   * {@inheritDoc}
   * <p>
   * This method object may conservatively return true in
   * cases where the specified rectangular area intersects a
   * segment of the path, but that segment does not represent a
   * boundary between the interior and exterior of the path.
   * Such a case may occur if some set of segments of the
   * path are retraced in the reverse direction such that the
   * two sets of segments cancel each other out without any
   * interior area between them.
   * To determine whether segments represent true boundaries of
   * the interior of the path would require extensive calculations
   * involving all of the segments of the path and the winding
   * rule and are thus beyond the scope of this implementation.
   *
   * @since 1.6
   */
  public final boolean intersects(Rectangle2D r) {
    return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
  }

  /**
   * {@inheritDoc}
   * <p>
   * The iterator for this class is not multi-threaded safe,
   * which means that this {@code Path2D} class does not
   * guarantee that modifications to the geometry of this
   * {@code Path2D} object do not affect any iterations of
   * that geometry that are already in process.
   *
   * @since 1.6
   */
  public final PathIterator getPathIterator(AffineTransform at,
      double flatness) {
    return new FlatteningPathIterator(getPathIterator(at), flatness);
  }

  /**
   * Creates a new object of the same class as this object.
   *
   * @return a clone of this instance.
   * @throws OutOfMemoryError if there is not enough memory.
   * @see java.lang.Cloneable
   * @since 1.6
   */
  public abstract Object clone();
  // Note: It would be nice to have this return Path2D
  // but one of our subclasses (GeneralPath) needs to
  // offer "public Object clone()" for backwards
  // compatibility so we cannot restrict it further.
  // REMIND: Can we do both somehow?

  /*
   * Support fields and methods for serializing the subclasses.
   */
  private static final byte SERIAL_STORAGE_FLT_ARRAY = 0x30;
  private static final byte SERIAL_STORAGE_DBL_ARRAY = 0x31;

  private static final byte SERIAL_SEG_FLT_MOVETO = 0x40;
  private static final byte SERIAL_SEG_FLT_LINETO = 0x41;
  private static final byte SERIAL_SEG_FLT_QUADTO = 0x42;
  private static final byte SERIAL_SEG_FLT_CUBICTO = 0x43;

  private static final byte SERIAL_SEG_DBL_MOVETO = 0x50;
  private static final byte SERIAL_SEG_DBL_LINETO = 0x51;
  private static final byte SERIAL_SEG_DBL_QUADTO = 0x52;
  private static final byte SERIAL_SEG_DBL_CUBICTO = 0x53;

  private static final byte SERIAL_SEG_CLOSE = 0x60;
  private static final byte SERIAL_PATH_END = 0x61;

  final void writeObject(java.io.ObjectOutputStream s, boolean isdbl)
      throws java.io.IOException {
    s.defaultWriteObject();

    float fCoords[];
    double dCoords[];

    if (isdbl) {
      dCoords = ((Path2D.Double) this).doubleCoords;
      fCoords = null;
    } else {
      fCoords = ((Path2D.Float) this).floatCoords;
      dCoords = null;
    }

    int numTypes = this.numTypes;

    s.writeByte(isdbl
        ? SERIAL_STORAGE_DBL_ARRAY
        : SERIAL_STORAGE_FLT_ARRAY);
    s.writeInt(numTypes);
    s.writeInt(numCoords);
    s.writeByte((byte) windingRule);

    int cindex = 0;
    for (int i = 0; i < numTypes; i++) {
      int npoints;
      byte serialtype;
      switch (pointTypes[i]) {
        case SEG_MOVETO:
          npoints = 1;
          serialtype = (isdbl
              ? SERIAL_SEG_DBL_MOVETO
              : SERIAL_SEG_FLT_MOVETO);
          break;
        case SEG_LINETO:
          npoints = 1;
          serialtype = (isdbl
              ? SERIAL_SEG_DBL_LINETO
              : SERIAL_SEG_FLT_LINETO);
          break;
        case SEG_QUADTO:
          npoints = 2;
          serialtype = (isdbl
              ? SERIAL_SEG_DBL_QUADTO
              : SERIAL_SEG_FLT_QUADTO);
          break;
        case SEG_CUBICTO:
          npoints = 3;
          serialtype = (isdbl
              ? SERIAL_SEG_DBL_CUBICTO
              : SERIAL_SEG_FLT_CUBICTO);
          break;
        case SEG_CLOSE:
          npoints = 0;
          serialtype = SERIAL_SEG_CLOSE;
          break;

        default:
          // Should never happen
          throw new InternalError("unrecognized path type");
      }
      s.writeByte(serialtype);
      while (--npoints >= 0) {
        if (isdbl) {
          s.writeDouble(dCoords[cindex++]);
          s.writeDouble(dCoords[cindex++]);
        } else {
          s.writeFloat(fCoords[cindex++]);
          s.writeFloat(fCoords[cindex++]);
        }
      }
    }
    s.writeByte(SERIAL_PATH_END);
  }

  final void readObject(java.io.ObjectInputStream s, boolean storedbl)
      throws java.lang.ClassNotFoundException, java.io.IOException {
    s.defaultReadObject();

    // The subclass calls this method with the storage type that
    // they want us to use (storedbl) so we ignore the storage
    // method hint from the stream.
    s.readByte();
    int nT = s.readInt();
    int nC = s.readInt();
    try {
      setWindingRule(s.readByte());
    } catch (IllegalArgumentException iae) {
      throw new java.io.InvalidObjectException(iae.getMessage());
    }

    pointTypes = new byte[(nT < 0) ? INIT_SIZE : nT];
    if (nC < 0) {
      nC = INIT_SIZE * 2;
    }
    if (storedbl) {
      ((Path2D.Double) this).doubleCoords = new double[nC];
    } else {
      ((Path2D.Float) this).floatCoords = new float[nC];
    }

    PATHDONE:
    for (int i = 0; nT < 0 || i < nT; i++) {
      boolean isdbl;
      int npoints;
      byte segtype;

      byte serialtype = s.readByte();
      switch (serialtype) {
        case SERIAL_SEG_FLT_MOVETO:
          isdbl = false;
          npoints = 1;
          segtype = SEG_MOVETO;
          break;
        case SERIAL_SEG_FLT_LINETO:
          isdbl = false;
          npoints = 1;
          segtype = SEG_LINETO;
          break;
        case SERIAL_SEG_FLT_QUADTO:
          isdbl = false;
          npoints = 2;
          segtype = SEG_QUADTO;
          break;
        case SERIAL_SEG_FLT_CUBICTO:
          isdbl = false;
          npoints = 3;
          segtype = SEG_CUBICTO;
          break;

        case SERIAL_SEG_DBL_MOVETO:
          isdbl = true;
          npoints = 1;
          segtype = SEG_MOVETO;
          break;
        case SERIAL_SEG_DBL_LINETO:
          isdbl = true;
          npoints = 1;
          segtype = SEG_LINETO;
          break;
        case SERIAL_SEG_DBL_QUADTO:
          isdbl = true;
          npoints = 2;
          segtype = SEG_QUADTO;
          break;
        case SERIAL_SEG_DBL_CUBICTO:
          isdbl = true;
          npoints = 3;
          segtype = SEG_CUBICTO;
          break;

        case SERIAL_SEG_CLOSE:
          isdbl = false;
          npoints = 0;
          segtype = SEG_CLOSE;
          break;

        case SERIAL_PATH_END:
          if (nT < 0) {
            break PATHDONE;
          }
          throw new StreamCorruptedException("unexpected PATH_END");

        default:
          throw new StreamCorruptedException("unrecognized path type");
      }
      needRoom(segtype != SEG_MOVETO, npoints * 2);
      if (isdbl) {
        while (--npoints >= 0) {
          append(s.readDouble(), s.readDouble());
        }
      } else {
        while (--npoints >= 0) {
          append(s.readFloat(), s.readFloat());
        }
      }
      pointTypes[numTypes++] = segtype;
    }
    if (nT >= 0 && s.readByte() != SERIAL_PATH_END) {
      throw new StreamCorruptedException("missing PATH_END");
    }
  }

  static abstract class Iterator implements PathIterator {

    int typeIdx;
    int pointIdx;
    Path2D path;

    static final int curvecoords[] = {2, 2, 4, 6, 0};

    Iterator(Path2D path) {
      this.path = path;
    }

    public int getWindingRule() {
      return path.getWindingRule();
    }

    public boolean isDone() {
      return (typeIdx >= path.numTypes);
    }

    public void next() {
      int type = path.pointTypes[typeIdx++];
      pointIdx += curvecoords[type];
    }
  }
}
